package com.lz.gmall.search.biz.impl;
import cn.hutool.core.util.PageUtil;
import com.lz.gmall.search.entity.SearchAttr;
import com.lz.gmall.search.vo.SearchOrderMapVo;

import com.lz.gmall.search.biz.GoodsBizService;
import com.lz.gmall.search.dto.SearchParamDTO;
import com.lz.gmall.search.entity.Goods;
import com.lz.gmall.search.repository.GoodsRepository;
import com.lz.gmall.search.vo.SearchRespAttrVo;
import com.lz.gmall.search.vo.SearchResponseVo;
import com.lz.gmall.search.vo.SearchTmVo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Service
public class GoodsBizServiceImpl implements GoodsBizService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchRestTemplate restTemplate;

    /**
     * 远程调用接口，用于上架商品，把商品信息存入ES数据库中
     * @param goods 商品信息 = 核心检索字段 + 要展示的数据
     * @return
     */
    @Override
    public void saveGoods(Goods goods) {
        goodsRepository.save(goods);
    }

    /**
     * 远程调用接口，用于下架商品，把商品信息从es库中删除
     * @param skuId
     * @return
     */
    @Override
    public void deleteGoodsBySkuId(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    /**
     * 根据查询条件进行查询es数据库
     * @param searchParamDTO 封装前端的查询条件
     * @return
     */
    @Override
    public SearchResponseVo search(SearchParamDTO searchParamDTO) {

        //多条件组合查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //构建查询条件
        //根据一级id进行查询
        if (searchParamDTO.getCategory1Id()!=null){
            boolQueryBuilder.must(QueryBuilders.termQuery("category1Id",searchParamDTO.getCategory1Id()));
        }
        //根据二级id进行查询
        if (searchParamDTO.getCategory2Id()!=null){
            boolQueryBuilder.must(QueryBuilders.termQuery("categoryI2d",searchParamDTO.getCategory2Id()));
        }
        //根据三级id进行查询
        if (searchParamDTO.getCategory3Id()!=null){
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id",searchParamDTO.getCategory3Id()));
        }
        //根据关键词进行查询
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        if (!StringUtils.isEmpty(searchParamDTO.getKeyword())){
            //设置高亮
            highlightBuilder.field("title").postTags("</font>").preTags("<font color = 'red'>");
            boolQueryBuilder.must(QueryBuilders.matchQuery("title",searchParamDTO.getKeyword()));
        }
        //根据品牌id进行查询
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            //前端传来的数据是 1:小米，进行处理
            Long tmId = Long.parseLong(trademark.split(":")[0]);
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId",tmId));
        }
        //根据品牌平台属性进行查询
        String[] props = searchParamDTO.getProps();
        if(props != null){
            for (String prop : props) {
                //遍历拿到所有的条件
                String[] strings = prop.split(":");
                long attrId = Long.parseLong(strings[0]);
                String attrValue = strings[1];
                //构建一个查询条件
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                //把所有的条件封装到boolQuery
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                boolQuery.must(QueryBuilders.termQuery("attrs.attrValue",attrValue));
                //通过所有的条件进行查询
                boolQueryBuilder.must(QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None));
            }
        }


        //进行分页
        PageRequest pageRequest = PageRequest.of(searchParamDTO.getPageNo() - 1, searchParamDTO.getPageSize());

        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .withHighlightBuilder(highlightBuilder)
                .build();


        //根据排序进行查询
        String order = searchParamDTO.getOrder();
        if (!StringUtils.isEmpty(order)&&!"null".equalsIgnoreCase(order)){
            //int sortFiled = Integer.parseInt(order.split(":")[0]);
            String sortFiled = order.split(":")[0];
            String sortType = order.split(":")[1];
            Sort sort = null;
            switch (sortFiled){
                case "1" :
                    sort = Sort.by(sortType.equalsIgnoreCase("desc")? Sort.Direction.DESC: Sort.Direction.ASC,"hotScore");
                    break;
                case "2" :
                    sort = Sort.by(sortType.equalsIgnoreCase("desc")? Sort.Direction.DESC: Sort.Direction.ASC,"price");
                    break;
            }
            nativeSearchQuery.addSort(sort);
        }

        //分组聚合查询,根据品牌条件从es数据库中对搜索的全部数据 进行分组 获取到数据
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("tmIdAgg").field("tmId").size(100);
        //一级子聚合
        termsAggregationBuilder.subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName").size(1));
        termsAggregationBuilder.subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl").size(1));
        nativeSearchQuery.addAggregation(termsAggregationBuilder);

        //分组聚合查询，根据属性条件从es数据库中对搜索的全部数据 进行分组 获取到数据
        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("attrAgg", "attrs");
        //一级子聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(100);
        //二级子聚合
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1));
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(100));
        nestedAggregationBuilder.subAggregation(attrIdAgg);
        nativeSearchQuery.addAggregation(nestedAggregationBuilder);



        //根据查询条件进行查询结果
        SearchHits<Goods> goodsSearchHits = restTemplate.search(nativeSearchQuery, Goods.class);

        // 对结果进行解析，将解析结果封装到SearchResponseVo对象
        SearchResponseVo searchResponseVo = parseResponse(goodsSearchHits , searchParamDTO) ;
        //SearchResponseVo searchResponseVo = new SearchResponseVo();

        return searchResponseVo;
    }

    /**
     * 根据商品id，每点击10次就更新es数据库中的热点数据
     * @param goodsId
     * @param hotScore
     */
    @Override
    public void updateHotScore(Long goodsId, Long hotScore) {
        Goods goods = goodsRepository.findById(goodsId).get();
        goods.setHotScore(hotScore);
        goodsRepository.save(goods);

    }

    //对结果进行解析，将解析结果封装到SearchResponseVo对象
    private SearchResponseVo parseResponse(SearchHits<Goods> goodsSearchHits, SearchParamDTO searchParamDTO) {

        SearchResponseVo searchResponseVo = new SearchResponseVo();
        //搜索条件
        searchResponseVo.setSearchParam(searchParamDTO);

        //品牌面包屑（品牌:小米），前端参数（trademark=1:小米）
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            String[] strings = trademark.split(":");
            String trademarkValue = strings[1];
            searchResponseVo.setTrademarkParam("品牌:"+trademarkValue);
        }

        //平台属性面包屑（运行内存:6G），前端的参数（23:6G:运行内存）
        String[] props = searchParamDTO.getProps();
        if (props!=null){
            List<SearchAttr> searchAttrList = new ArrayList<>();
            for (String prop : props) {
                String[] strings = prop.split(":");
                String attrId = strings[0];
                String attrValue = strings[1];
                String attrName = strings[2];
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(Long.parseLong(attrId));
                searchAttr.setAttrValue(attrValue);
                searchAttr.setAttrName(attrName);
                searchAttrList.add(searchAttr);
            }
            searchResponseVo.setPropsParamList(searchAttrList);
        }

        //排序信息
        String orderString = searchParamDTO.getOrder();
        //如果这里不进行默认值设定，在直接使用的时候，会报错，页面不显示数据
        //这是因为我们前端访问列表页面的时候，没有传递order的值，我们这里就无法给前端传递该moder的数据，传递的null，前端使用了属性，所以报错
        SearchOrderMapVo searchOrderMapVo = new SearchOrderMapVo("1","asc");//默认条件下
        if (!StringUtils.isEmpty(orderString) && !"null".equalsIgnoreCase(orderString)){
            String[] strings = orderString.split(":");
            String type = strings[0];
            String sort = strings[1];
            searchOrderMapVo.setType(type);
            searchOrderMapVo.setSort(sort);
        }
        searchResponseVo.setOrderMap(searchOrderMapVo);

        //商品的详细数据,从es数据库中查询到goods商品传递给前端
        List<SearchHit<Goods>> searchHits = goodsSearchHits.getSearchHits();
        List<Goods> goodsList = new ArrayList<>();
        for (SearchHit<Goods> goodsSearchHit : searchHits) {
            Goods goods = goodsSearchHit.getContent();
            if (!StringUtils.isEmpty(searchParamDTO.getKeyword())){
                //高亮展示
                String highlightTitle = goodsSearchHit.getHighlightField("title").get(0);
                goods.setTitle(highlightTitle);
            }
            goodsList.add(goods);
        }
        searchResponseVo.setGoodsList(goodsList);

        //设置总页数
        long totalHits = goodsSearchHits.getTotalHits();//获取总记录条数
        //使用hutool工具类进行计算总页码和当前页码
        int totalPage = PageUtil.totalPage((int)totalHits, searchParamDTO.getPageSize());
        searchResponseVo.setTotalPages(Long.parseLong(String.valueOf(totalPage)));
        //设置当前页码
        searchResponseVo.setPageNo(searchParamDTO.getPageNo());

        //设置平台搜索中的品牌列表，是根据条件从es数据库中搜索的全部数据 进行分组 获取到数据
        Aggregations aggregations = goodsSearchHits.getAggregations();
        ParsedLongTerms parsedLongTerms = aggregations.get("tmIdAgg");
        List<? extends Terms.Bucket> longTermsBuckets = parsedLongTerms.getBuckets();
        List<SearchTmVo> searchTmVoList = new ArrayList<>();
        for (Terms.Bucket bucket : longTermsBuckets) {
            String tmId = bucket.getKeyAsString();
            ParsedStringTerms tmNameTerms = bucket.getAggregations().get("tmNameAgg");
            String tmName = tmNameTerms.getBuckets().get(0).getKeyAsString();
            ParsedStringTerms tmLogoUrlTerms = bucket.getAggregations().get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlTerms.getBuckets().get(0).getKeyAsString();
            //数据封装
            SearchTmVo searchTmVo = new SearchTmVo();
            searchTmVo.setTmId(Long.parseLong(tmId));
            searchTmVo.setTmName(tmName);
            searchTmVo.setTmLogoUrl(tmLogoUrl);
            searchTmVoList.add(searchTmVo);
        }
        searchResponseVo.setTrademarkList(searchTmVoList);

        //设置平台搜索中的平台属性，根据查询条件 查询到的数据 进行分组
        ParsedNested parsedNested = aggregations.get("attrAgg");
        Aggregations nestedAggregations = parsedNested.getAggregations();
        ParsedLongTerms attrIdAggTerms = nestedAggregations.get("attrIdAgg");
        List<SearchRespAttrVo> attrsList = new ArrayList<>();
        for (Terms.Bucket bucket : attrIdAggTerms.getBuckets()) {
            String attrId = bucket.getKeyAsString();
            //attrName只有一个桶
            ParsedStringTerms attrNameAggTerms = bucket.getAggregations().get("attrNameAgg");
            String attrName = attrNameAggTerms.getBuckets().get(0).getKeyAsString();
            //attrValue有多个桶
            ParsedStringTerms attrValueAggTerms = bucket.getAggregations().get("attrValueAgg");
            List<String> attrValueList = new ArrayList<>();
            for (Terms.Bucket valueAggBucket : attrValueAggTerms.getBuckets()) {
                String attrValue = valueAggBucket.getKeyAsString();
                attrValueList.add(attrValue);
            }
            //封装数据
            SearchRespAttrVo searchRespAttrVo = new SearchRespAttrVo();
            searchRespAttrVo.setAttrName(attrName);
            searchRespAttrVo.setAttrId(Long.parseLong(attrId));
            searchRespAttrVo.setAttrValueList(attrValueList);
            attrsList.add(searchRespAttrVo);
        }
        searchResponseVo.setAttrsList(attrsList);

        //设置url参数，他表示的是之前路径的参数，当我们点击其他的搜索属性
        //前端发送请求是 用之前的url+新的参数向后端发送请求
        String url = getUrlParam(searchParamDTO);
        searchResponseVo.setUrlParam(url);

        return searchResponseVo;
    }

    //对UrlParam进行处理，获取之前条件的url路径
    private String getUrlParam(SearchParamDTO searchParamDTO) {
        StringBuilder url = new StringBuilder();
        url.append("list.html?");

        if (searchParamDTO.getCategory1Id()!=null){
            url.append("&category1Id=").append(searchParamDTO.getCategory1Id());
        }
        if (searchParamDTO.getCategory2Id()!=null){
            url.append("&category2Id=").append(searchParamDTO.getCategory2Id());
        }
        if (searchParamDTO.getCategory3Id()!=null){
            url.append("&category3Id=").append(searchParamDTO.getCategory3Id());
        }

        if (!StringUtils.isEmpty(searchParamDTO.getKeyword())){
            url.append("&keyword=").append(searchParamDTO.getKeyword());
        }
        //前端传来的数据是 trademark=4:小米，进行处理
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            //String[] strings = trademark.split(":");
            url.append("&trademark=").append(trademark);
        }
        //前端的数据 &props=106:安卓手机:手机一级&props=23:8G:运行内存
        String[] props = searchParamDTO.getProps();
        if (props!=null){
            for (String prop : props) {
                //String[] strings = prop.split(":");
                url.append("&props=").append(prop);
            }
        }
        //排序条件,&order=2:desc
        //String order = searchParamDTO.getOrder();
        //url.append("&order").append(order);

        return url.toString();
    }
}
